#region Microsoft Public License
/*
 * (c) Copyright Chenjun Wu. http://utablesdk.codeplex.com
 * 
 * This source is subject to the Microsoft Public License (Ms-PL).
 * Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
 * All other rights reserved.
 */
#endregion

﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UTable.Objects;
using System.Collections.ObjectModel;
using System.Windows;
using UTable.Objects.Controls;

namespace UTable.Input.MultiTouch
{
    public class MultiTouchInputMapper: IInputMapper
    {
        private IVisualTree _visualTree;

        private Dictionary<int, IVisualNode> _fingerMap = new Dictionary<int, IVisualNode>();    // record the current visual node each finger is on

        private List<int> _capturedFingers = new List<int>();

        private List<int> _golbalCapturedFingers = new List<int>();

        private Dictionary<int, IVisualNode> _capturedFingers2 = new Dictionary<int, IVisualNode>();

        private bool IsDescendentOf(IVisualNode root, IVisualNode descendent)
        {
            if (root == descendent)
                return true;
            for (int i = 0; i < VisualTree.GetChildrenCount(root); i++)
            {
                if (IsDescendentOf(VisualTree.GetChild(root, i), descendent))
                    return true;
            }
            return false;
        }

        private void addFingerEvent(FingerEventArgs f, IVisualNode target, Collection<ManagedInputEventArgs> inputs)
        {
			f.Timestamp = InputEventArgs.GetTimestamp();
            if (_capturedFingers2.ContainsKey(f.FingerID))
            {
                // see whether the target is on the tree rooted by the capturing node
                // if so, then do all the things in the normal way
                // else, the target should changed to be the capturing node
                if (!(IsDescendentOf(_capturedFingers2[f.FingerID], target)))
                {
                    if (!(f.EventType == FingerEventType.FINGER_IN_EVENT || f.EventType == FingerEventType.FINGER_OUT_EVENT))
                    {
                        target = _capturedFingers2[f.FingerID];
                    }
                    else
                        return;
                }
            }

            ManagedInputEventArgs dest = null;
            // Console.WriteLine(f.Position);
            foreach (ManagedInputEventArgs input in inputs)
            {
                if (input.Receiver == target)
                {
                    f.Position = VisualTree.TranslatePoint(
                        f.Position, target);
                    (input.Args as MultiTouchEventArgs).FingerEvents.Add(f);
                    dest = input;
                    return;
                }
            }

            if (dest == null)
            {
                MultiTouchEventArgs input = new MultiTouchEventArgs(InputEventArgs.GetTimestamp());
                f.Position = VisualTree.TranslatePoint(
                    f.Position, target);
                input.FingerEvents.Add(f);
                inputs.Add(new ManagedInputEventArgs(target, input));
            }
        }

        #region IInputMapper Members

        public IVisualTree VisualTree
        {
            get
            {
                return _visualTree;
            }
            set
            {
                _visualTree = value;
            }
        }

        public virtual Collection<ManagedInputEventArgs> MapInput(InputEventArgs args)
        {
            if (!(args is MultiTouchEventArgs))
                return new Collection<ManagedInputEventArgs>();

            // set the input object and control
            Collection<ManagedInputEventArgs> inputs = new Collection<ManagedInputEventArgs>();

            foreach (FingerEventArgs f in (args as MultiTouchEventArgs).FingerEvents)
            {
				if (UTableHelper.InGlobalScope())
				{
					f.GlobalPosition = f.Position;
				}

                IVisualNode target;
                if (_capturedFingers.Contains(f.FingerID))
                {
                    target = _fingerMap[f.FingerID];
                    if (_golbalCapturedFingers.Contains(f.FingerID))
                        f.Captured = true;
                    if (f.EventType == FingerEventType.FINGER_UP_EVENT || f.EventType == FingerEventType.FINGER_OUT_EVENT)
                    {
                        _capturedFingers.Remove(f.FingerID);
                        if (_golbalCapturedFingers.Contains(f.FingerID))
                            _golbalCapturedFingers.Remove(f.FingerID);
                        (target as IInputNode).UncaptureFinger(f.FingerID);
                    }
                }
                else
                {
                    target = VisualTree.HitTest(new PointHitTestParameters(f.Position)).VisualHit;
                    if (f.EventType == FingerEventType.FINGER_DOWN_EVENT || f.EventType == FingerEventType.FINGER_IN_EVENT)
                    {
                        if (!_fingerMap.ContainsKey(f.FingerID))
                            _fingerMap.Add(f.FingerID, target);
                        else
                            _fingerMap[f.FingerID] = target;
                    }
                    else
                    {
                        if (_fingerMap.ContainsKey(f.FingerID) && _fingerMap[f.FingerID] != target)
                        {
                            FingerEventArgs fingerIn = new FingerEventArgs(FingerEventType.FINGER_IN_EVENT, f.FingerID, f.Position);
                            fingerIn.UserID = f.UserID;
                            addFingerEvent(fingerIn, target, inputs);
                            FingerEventArgs fingerOut = new FingerEventArgs(FingerEventType.FINGER_OUT_EVENT, f.FingerID, f.Position);
                            fingerOut.UserID = f.UserID;
                            addFingerEvent(fingerOut, _fingerMap[f.FingerID], inputs);
                            _fingerMap[f.FingerID] = target;
                        }
                    }
                }
                addFingerEvent(f, target, inputs);
                if (f.EventType == FingerEventType.FINGER_UP_EVENT)
                {
                    _fingerMap.Remove(f.FingerID);
                    if (_capturedFingers2.ContainsKey(f.FingerID))
                        _capturedFingers2.Remove(f.FingerID);
                    if (_capturedFingers.Contains(f.FingerID))
                        _capturedFingers.Remove(f.FingerID);
                    if (_golbalCapturedFingers.Contains(f.FingerID))
                        _golbalCapturedFingers.Remove(f.FingerID);
                }
            }
            return inputs;
        }

        public virtual bool CaptureFinger(IVisualNode node, int fingerId, bool fromGlobal)
        {
            if (!_fingerMap.ContainsKey(fingerId))
                return false;
            if (_capturedFingers.Contains(fingerId))
            {
                if (node == _fingerMap[fingerId])
                    return true;
                else
                    return false;
            }
            if (fromGlobal)
                _golbalCapturedFingers.Add(fingerId);
            else if (node is IControl && _fingerMap.ContainsKey(fingerId))
            {
                // send the finger leave event to the controls that this finger is on
                // firstly get the position of current finger
                IVisualNode previous_node = _fingerMap[fingerId];
                FingerEventArgs fingerLeave = new FingerEventArgs(FingerEventType.FINGER_OUT_EVENT, fingerId, new Point(0, 0));
                MultiTouchEventArgs input = new MultiTouchEventArgs(InputEventArgs.GetTimestamp());
                input.FingerEvents.Add(fingerLeave);
                input.OriginalSource = node;
                if (InputMapped != null)
                    InputMapped(new ManagedInputEventArgs(previous_node, input));
            }

            _capturedFingers.Add(fingerId);
            _fingerMap[fingerId] = node;
            return true;
        }

        public virtual bool UncaptureFinger(IVisualNode node, int fingerId)
        {
            if (!_fingerMap.ContainsKey(fingerId) || !_capturedFingers.Contains(fingerId))
                return true;
            if (node == _fingerMap[fingerId])
            {
                _capturedFingers.Remove(fingerId);
				_golbalCapturedFingers.Remove(fingerId);
				if (node is IControl)
				{
					// send the finger in event to the controls that this finger is on
					// firstly get the position of current finger
					FingerEventArgs fingerIn = new FingerEventArgs(FingerEventType.FINGER_IN_EVENT, fingerId, new Point(0, 0));
					MultiTouchEventArgs input = new MultiTouchEventArgs(InputEventArgs.GetTimestamp());
					input.FingerEvents.Add(fingerIn);
					Collection<ManagedInputEventArgs> managedInput = MapInput(input);
					if (InputMapped != null)
						InputMapped(managedInput[0]);
				}

                return true;
            }
            else
                return false;
        }

		/// <summary>
		/// Capture the finger for the node
		/// What is different from the CaptureFinger Method is that the finger captured can also be mapped to the descendants of the node
		/// </summary>
        public virtual bool CaptureFinger2(IVisualNode node, int fingerId)
        {
            if (!_fingerMap.ContainsKey(fingerId))
                return false;
            _capturedFingers2.Add(fingerId, node);
            if (node is IControl && _fingerMap.ContainsKey(fingerId) && !IsDescendentOf(node, _fingerMap[fingerId]))
            {
                // send the finger leave event to the controls that this finger is on
                // firstly get the position of current finger
                IVisualNode previous_node = _fingerMap[fingerId];
                FingerEventArgs fingerLeave = new FingerEventArgs(FingerEventType.FINGER_OUT_EVENT, fingerId, new Point(0, 0));
                MultiTouchEventArgs input = new MultiTouchEventArgs(InputEventArgs.GetTimestamp());
                input.FingerEvents.Add(fingerLeave);
                input.OriginalSource = node;
                if (InputMapped != null)
                    InputMapped(new ManagedInputEventArgs(previous_node, input));
            }
            return true;
        }

        public virtual bool UncaptureFinger2(IVisualNode node, int fingerId)
        {
            if (!_fingerMap.ContainsKey(fingerId) || !_capturedFingers2.ContainsKey(fingerId))
                return true;
            _capturedFingers2.Remove(fingerId);
            return true;
        }

        public event ManagedInputEventHandler InputMapped;

        #endregion
    }

    public delegate bool CaptureFingerHandler(IVisualNode node, int fingerId, bool fromGlobal);

    public delegate bool UncaptureFingerHandler(IVisualNode node, int fingerId);

    public delegate bool CaptureFingerHandler2(IVisualNode node, int fingerId);

    public delegate bool UncaptureFingerHandler2(IVisualNode node, int fingerId);
}
